﻿#pragma once

#include <tuple>

#include "type_trait_helper.h"

namespace nggs
{
    // ConstForEachInTuple/ForEachInTuple
#if __cplusplus >= 201703L

    template <typename... Args, typename Func>
    void ConstEachInTuple(const std::tuple<Args...> &tuple, Func &&func)
    {
        std::apply([&func](auto &&...element)
                   { ((func(element)), ...); },
                   tuple);
    }

    template <typename... Args, typename Func>
    void EachInTuple(std::tuple<Args...> &tuple, Func &&func)
    {
        std::apply([&func](auto &&...element)
                   { ((func(element)), ...); },
                   tuple);
    }

#elif __cplusplus >= 201402L

    template <typename Func, typename... Args, std::size_t... I>
    void ConstEachInTupleImpl(const std::tuple<Args...> &tuple, Func &&func, std::index_sequence<I...>)
    {
        using expander = int[];
        (void)expander{
            0, ((void)func(std::get<I>(tuple)), 0)...};
    }

    template <typename Func, typename... Args>
    void ConstEachInTuple(const std::tuple<Args...> &tuple, Func &&func)
    {
        ConstEachInTupleImpl(tuple, func, std::make_index_sequence<sizeof...(Args)>());
    }

    template <typename Func, typename... Args, std::size_t... I>
    void EachInTupleImpl(std::tuple<Args...> &tuple, Func &&func, std::index_sequence<I...>)
    {
        using expander = int[];
        (void)expander{
            0, ((void)func(std::get<I>(tuple)), 0)...};
    }

    template <typename Func, typename... Args>
    void EachInTuple(std::tuple<Args...> &tuple, Func &&func)
    {
        EachInTupleImpl(tuple, func, std::make_index_sequence<sizeof...(Args)>());
    }

#elif __cplusplus >= 201103L || _MSC_VER == 1800

    template <typename Func, std::size_t... Indexes, typename... Args>
    void ConstEachInTupleImpl(const std::tuple<Args...> &tuple, Func &&func, index_sequence<Indexes...>)
    {
        auto l = {(func(std::get<Indexes>(tuple)), 0)...};
        (void)l; // 去告警
    }

    template <typename Func, std::size_t... Indexes, typename... Args>
    void EachInTupleImpl(std::tuple<Args...> &tuple, Func &&func, index_sequence<Indexes...>)
    {
        auto l = {(func(std::get<Indexes>(tuple)), 0)...};
        (void)l; // 去告警
    }

    template <typename Func, typename... Args>
    void ConstEachInTuple(const std::tuple<Args...> &tuple, Func &&func)
    {
        ConstEachInTupleImpl(tuple, func, make_index_sequence<sizeof...(Args)>());
    }

    template <typename Func, typename... Args>
    void EachInTuple(std::tuple<Args...> &tuple, Func &&func)
    {
        EachInTupleImpl(tuple, func, make_index_sequence<sizeof...(Args)>());
    }

#else

    static_assert(true, "please use c++11 or higher");

#endif
    // !ConstEachInTuple/EachInTuple

    // GetTupleElementByType/SetTupleElementByType
#if __cplusplus >= 201402L

    template <class T, std::size_t N, class... Args>
    struct IndexOfTuple;

    template <class T, std::size_t N, class... Args>
    struct IndexOfTuple<T, N, T, Args...>
    {
        static constexpr auto value = N;
    };

    template <class T, std::size_t N, class U, class... Args>
    struct IndexOfTuple<T, N, U, Args...>
    {
        static constexpr auto value = IndexOfTuple<T, N + 1, Args...>::value;
    };

    // template <class T, std::size_t N>
    // struct IndexOfTuple<T, N>
    // {
    //     static constexpr auto value = -1;
    //     static_assert(value != -1, "the type is not exist");
    // };

    template <class T, class... Args>
    T GetTupleElementByType(const std::tuple<Args...> &t)
    {
        return std::get<IndexOfTuple<T, 0, Args...>::value>(t);
    }

    template <class T, class... Args>
    void SetTupleElementByType(std::tuple<Args...> &t, T e)
    {
        std::get<IndexOfTuple<T, 0, Args...>::value>(t) = e;
    }

#elif __cplusplus >= 201103L || _MSC_VER == 1800

    template <class T, std::size_t N, class... Args>
    struct IndexOfTuple;

    template <class T, std::size_t N, class... Args>
    struct IndexOfTuple<T, N, T, Args...> : std::integral_constant<int, N>
    {
    };

    template <class T, std::size_t N, class U, class... Args>
    struct IndexOfTuple<T, N, U, Args...> : std::integral_constant<int, IndexOfTuple<T, N + 1, Args...>::value>
    {
    };

    template <class T, std::size_t N>
    struct IndexOfTuple<T, N> : std::integral_constant<int, -1>
    {
    };

    template <class T, class... Args>
    T GetTupleElementByType(const std::tuple<Args...> &t)
    {
        return std::get<IndexOfTuple<T, 0, Args...>::value>(t);
    }

    template <class T, class... Args>
    void SetTupleElementByType(std::tuple<Args...> &t, T e)
    {
        std::get<IndexOfTuple<T, 0, Args...>::value>(t) = e;
    }

#else

    static_assert(true, "please use c++11 or higher");

#endif
    // !GetTupleElementByType/SetTupleElementByType

} // !namespace nggs
